package com.sise.dubbo.redislock.controller;

import com.sise.dubbo.redislock.dto.GoodsDTO;
import com.sise.dubbo.redislock.service.GoodsService;
import com.sise.dubbo.redislock.service.LockDaemonThread;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.JedisCommands;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * 通过setnx来实现
 *
 * @author idea
 * @data 2019/2/2
 */
@RestController
@RequestMapping(value = "/distributedLock")
public class DistributedLockController {


    @Autowired
    private GoodsService goodsService;

    @Resource
    private RedisTemplate<String, GoodsDTO> redisTemplate;


    @GetMapping(value = "/setnx/exchange")
    public boolean exchangeBySetNX(Integer goodId) {
//        System.out.println(Thread.currentThread().getName());
        String prefix = "lock——";
        try {
            //获取锁
            lock(prefix + goodId);
            goodsService.exchangeGoods(goodId);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            //释放锁的操作
            unlock(prefix + goodId);
        }
    }


    /**
     * 通过setnx方式来进行加锁
     *
     * @param key
     */
    private void lock(String key) {
        Long result = setNx(key, "value", 5);
        Thread lockDaemonThread = new Thread(new LockDaemonThread(key,redisTemplate));
        lockDaemonThread.setDaemon(true);
        lockDaemonThread.start();
        while (result != 1) {
            //cas自旋转加锁处理
            result = setNx(key, "value", 5);
        }
    }

    /**
     * 解除锁
     *
     * @param key
     */
    private void unlock(String key) {
        redisTemplate.delete(key);
    }


    /**
     * redis里面的setnx操作(定时锁的时间，以秒为单位)
     */
    private Long setNx(String key, String value, Integer time) {
        RedisCallback<Long> callback = (connection) -> {
            JedisCommands jedisCommands = (JedisCommands) connection.getNativeConnection();
            return jedisCommands.setnx(key, value);
        };
        Long result = redisTemplate.execute(callback);
        redisTemplate.expire(key, time, TimeUnit.SECONDS);
        return result;
    }


}
